﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TeleportShotManager : MonoBehaviour, IResettable
{

    public float lateralForceFactor = 10.0f;
    public float minimumVelocity = 1.0f;
    public int maxBounces = 10;

    private int bounces = 0;
    private bool hasHadVelocity = false;
    private bool forceImmediateFinish = false;

    private GyroscopeReader gyroscopeReader;

    public float particleTrailTimeDelta = 0.25f;    // Default the trail updates to 4 times per second
    private float particleTrailCurrentTime = 0.0f;

    /// <summary>
    /// Variables for debugging lateral forces. Remove when no longer needed.
    /// </summary>
    Vector3 lateralDirection = Vector3.zero;
    Vector3 adjustment = Vector3.zero;

    private Vector3 turningForce = Vector3.zero;

    private RobotPositionValidation validator = null;

    private List<Vector3> bouncePositions = new List<Vector3>();
    private List<Vector3> tentativeBouncePositions = new List<Vector3>();

	private AudioSource bounceAudio;

    // Use this for initialization
    void Start()
    {
        gyroscopeReader = GameObject.FindGameObjectWithTag("gyroscope").GetComponent<GyroscopeReader>();
        ResetItemList.Instance.AddItem(this);
        validator = this.transform.FindChild("RobotPositionValidator").GetComponent<RobotPositionValidation>();
        FollowActionPriorityQueue.Instance.AddFollowPosition(new TeleportShotPrioritizedFollowPosition(this));

		bounceAudio = transform.FindChild ("BounceNoise").GetComponent<AudioSource> ();
	}

    // Update is called once per frame
    void Update()
    {

        if (this.rigidbody.velocity.magnitude > 0)
        {
            hasHadVelocity = true;
        }

        if (hasHadVelocity)
        {
            particleTrailCurrentTime += Time.deltaTime;
            if (particleTrailCurrentTime >= particleTrailTimeDelta)
            {
                if (inInvalidArea)
                {
                    tentativeBouncePositions.Add(transform.position);
                }
                else
                {
                    bouncePositions.Add(transform.position);
                }
                particleTrailCurrentTime -= particleTrailTimeDelta;
            }

            if (this.rigidbody.velocity.magnitude < minimumVelocity || bounces >= maxBounces || forceImmediateFinish)
            {
                if (validator.validPosition != Vector3.zero)
                {
                    // Start the teleportation sequence
                    Transform particleCloud = this.transform.FindChild("RobotCloud");
                    particleCloud.parent = null;
                    particleCloud.GetComponent<TeleportationManager>().Execute(bouncePositions);
                }
                Destroy(this.gameObject);
                return;
            }
        }

        // Calculate a force to apply to this object based on the current z rotation...

        Vector3 currentVelocity = this.rigidbody.velocity;
        lateralDirection = Vector3.Cross(currentVelocity, Camera.main.transform.forward);   // TODO: Make sure that camera-forward is always what we want!
        adjustment = Quaternion.AngleAxis(gyroscopeReader.rotationChangeDegrees.z, Camera.main.transform.forward) * currentVelocity;
        float magnitude = Vector3.Dot(adjustment, lateralDirection);
        turningForce = lateralDirection.normalized * magnitude * lateralForceFactor;

        this.rigidbody.AddForce(turningForce);
        
    }

    void LateUpdate()
    {
        if (slowdown)
        {
            this.GetComponent<LateSpeedAdjust>().SetAdjustment(slowdownFactor);
        }
    }

    void OnDrawGizmos()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawLine(this.transform.position, this.transform.position + this.rigidbody.velocity);

        Gizmos.color = Color.red;
        Gizmos.DrawLine(this.transform.position, this.transform.position + turningForce);

        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(this.transform.position, this.transform.position + lateralDirection);

        Gizmos.color = Color.blue;
        Gizmos.DrawLine(this.transform.position, this.transform.position + adjustment);
    }

    bool destroyed = false;
    public bool IsDestroyed { get { return destroyed; } }

    public void Reset()
    {
        if (!destroyed && this.gameObject != null)
        {
            destroyed = true;
            Destroy(this.gameObject);
        }
    }

    public bool PersistAfterReset()
    {
        return false;
    }

    void OnDestroy()
    {
        ResetItemList.Instance.RemoveItem(this);
        destroyed = true;
    }

    bool slowdown = false;

    public void SetSlowdown(bool value)
    {
        slowdown = value;
    }



    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "target")
        {
            SetSlowdown(true);
        }
        else
        {
			if (bounceAudio.isPlaying)
				bounceAudio.Stop ();
			bounceAudio.Play ();
            bounces++;
        }
    }

    void OnCollisionExit(Collision collision)
    {
        if (collision.gameObject.tag == "target")
        {
            SetSlowdown(false);
        }
    }

    void OnTriggerEnter(Collider collider)
    {
        if (collider.tag == "target")
        {
            SetSlowdown(true);
        }
    }

    void OnTriggerExit(Collider collider)
    {
        if (collider.tag == "target")
        {
            SetSlowdown(false);
        }
    }

    internal void FinishTeleport()
    {
        forceImmediateFinish = true;
    }

    private bool inInvalidArea = false;
    public float slowdownFactor = 0.95f;

    internal void OnInvalidAreaEnter()
    {
        inInvalidArea = true;
    }

    internal void OnInvalidAreaExit()
    {
        inInvalidArea = false;
        foreach (Vector3 position in tentativeBouncePositions)
        {
            bouncePositions.Add(position);
        }
        tentativeBouncePositions.Clear();
    }
}
